package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.exception.Assert;
import com.atguigu.srb.base.result.ResponseEnum;
import com.atguigu.srb.base.utils.JwtUtils;
import com.atguigu.srb.core.enums.LendStatusEnum;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.pojo.entity.LendItem;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.LendItemService;
import com.atguigu.srb.core.service.LendService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.utils.LendNoUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2023-01-04
 */
@Service
@Slf4j
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {
    @Resource
    UserInfoService userInfoService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendService lendService;
    @Override
    public String invest(String token, String lendId, BigDecimal investAmount) {
//        1、校验：
//        用户状态
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = userInfoService.getById(userId);
        Assert.assertTrue(userInfo.getStatus()!=1, ResponseEnum.LOGIN_LOKED_ERROR);
//        用户账户余额
        UserAccount userAccount = userAccountService.getUserAccount(userId);
        Assert.assertTrue(userAccount.getAmount().doubleValue()<investAmount.doubleValue()
            ,ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);
//        标的状态：募资中
        Lend lend = lendService.getById(lendId);
        Assert.assertTrue(lend.getStatus()!= LendStatusEnum.INVEST_RUN.getStatus()
                ,ResponseEnum.LEND_INVEST_ERROR);
//        标的是否满标： 用户本次投资金额+标的已投金额<=标的总金额
        Assert.assertTrue(lend.getAmount().doubleValue()< lend.getInvestAmount()
                        .add(investAmount).doubleValue()
                ,ResponseEnum.LEND_FULL_SCALE_ERROR);
//        2、保存投资记录：lend_item
        LendItem lendItem = new LendItem();
        lendItem.setLendItemNo(LendNoUtils.getLendItemNo());
        lendItem.setLendId(lend.getId());
        lendItem.setInvestUserId(userId);
        lendItem.setInvestName(userInfo.getName());
        lendItem.setInvestAmount(investAmount);
        lendItem.setLendYearRate(lend.getLendYearRate());
        lendItem.setInvestTime(new Date());
        lendItem.setLendStartDate(lend.getLendStartDate());
        lendItem.setLendEndDate(lend.getLendEndDate());
        //计算预期收益： 根据标的的不同方式计算 月化率*期数*投资金额
        BigDecimal interestCount = lendService.getInterestCount(investAmount, lendId);
        lendItem.setExpectAmount(interestCount);
        lendItem.setRealAmount(new BigDecimal(0));
        lendItem.setStatus(0);
        this.save(lendItem);
//        3、生成访问hfb投资的接口自动提交表单让浏览器访问

        Map<String, Object> map = new HashMap<>();
        map.put("agentId",HfbConst.AGENT_ID);
        map.put("voteBindCode",userInfo.getBindCode());//投资人hfb账户bindCode
        UserInfo borrowUserInfo = userInfoService.getById(lend.getUserId());
        map.put("benefitBindCode",borrowUserInfo.getBindCode()); //标的借款用户hfb账户
        map.put("agentProjectCode",lend.getLendNo());//投资项目编号
        map.put("agentProjectName",lend.getTitle());
        //
//        lendItem.getLendItemNo()
//        map.put("agentBillNo",LendNoUtils.getLendItemNo());//本次投资的唯一编号
        map.put("agentBillNo",lendItem.getLendItemNo());//本次投资的唯一编号
        map.put("voteAmt",investAmount);//投资金额
        map.put("projectAmt",lend.getAmount());//标的总金额
        map.put("voteFeeAmt","0");//手续费
        map.put("notifyUrl",HfbConst.INVEST_NOTIFY_URL);
        map.put("returnUrl",HfbConst.INVEST_RETURN_URL);
        map.put("timestamp",System.currentTimeMillis());
        map.put("sign",RequestHelper.getSign(map));
        return FormHelper.buildForm(HfbConst.INVEST_URL,map);
    }
    //投资回调
    @Override
    public String investNotify(HttpServletRequest request) {
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
//        System.out.println(map);
        //1、验签
        if(!RequestHelper.isSignEquals(map)){
            log.error("投资回调验签失败: {}", JSON.toJSONString(map));
            return "fail";
        }
        //2、验证resultCode
        if(!"0001".equals(map.get("resultCode").toString())){
            log.error("投资业务失败: {}", JSON.toJSONString(map));
            return "fail";
        }
        //保证幂等性： 使用回调传入的 lendItem的编号查询lendItem 如果状态是已支付 不再处理业务
        //查询投资记录：
        LendItem lendItem = this.getOne(Wrappers.lambdaQuery(LendItem.class)
                .eq(LendItem::getLendItemNo, map.get("agentBillNo")));
        if(lendItem.getStatus()==1){
            return "success";
        }
        //3、验证投资金额是否正确
        if(lendItem.getInvestAmount().doubleValue()!=
                new BigDecimal(map.get("voteAmt").toString()).doubleValue() ){
            log.error("投资金额错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        //4、更新投资记录的状态为已支付
        lendItem.setStatus(1);
        this.updateById(lendItem);
        //5、更新标的的投资人数+已投金额
        Lend lend = lendService.getById(lendItem.getLendId());
        lend.setInvestNum(lend.getInvestNum()+1);
        lend.setInvestAmount(lend.getInvestAmount().add(lendItem.getInvestAmount()));
        lendService.updateById(lend);
        //6、冻结用户的投资金额
        //账户 - 投资金额             冻结金额 + 投资金额
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, lendItem.getInvestUserId()));
        userAccount.setAmount(userAccount.getAmount().subtract(lendItem.getInvestAmount()));
        userAccount.setFreezeAmount(userAccount.getFreezeAmount().add(lendItem.getInvestAmount()));
        userAccountService.updateById(userAccount);
        return "success";
    }
}
